package com.molichuxing.services.aspect;

import com.alibaba.fastjson.JSONObject;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @prouect_name: order-service
 * @class_name: ${CLASS_NAME}
 * @description: ${DESCRIPTION}
 * @author: 林亿建
 * @create_date: 2019年01月09日
 * @modify_date: 2019年01月09日
 **/
@Component("globalAspect")
@Aspect
public class GlobalAspect {
    private static final Logger logger = LoggerFactory.getLogger(GlobalAspect.class);

    private final static String LINEFEED = "\r\n";
    private final static String NEWLINE = LINEFEED + "      ========>";

    /**
     * @within(org.springframework.stereotype.Service)，拦截带有 @Service 注解的类的所有方法
     */
    @Pointcut("@within(org.springframework.stereotype.Service) && execution(public * *(..))")
    private void servicePointcut() {
    }

    /**
     * 拦截service层异常，记录异常日志，并设置对应的异常信息 目前只拦截Exception，是否要拦截Error需再做考虑
     *
     * @param e 异常对象
     */
    // @AfterThrowing(pointcut = "servicePointcut()", throwing = "e")
    // public void afterThrowing(JoinPoint joinPoint, Exception e) {
    // try {
    // String method = joinPoint.getSignature().toString();
    // List args = Arrays.asList(joinPoint.getArgs());
    // Map<String, Object> argsValue = getArgsNameValue(joinPoint);
    // LOGGER.error("调用方法:{0},参数：{1},参数值:{2},发生异常，异常信息:{3}", method, args,
    // argsValue.toString(),
    // e.fillInStackTrace());
    // throw new RuntimeException(e.getMessage());
    // } catch (NotFoundException ex) {
    // LOGGER.error(ex.getMessage());
    // throw new RuntimeException("服务提供者的服务实现AOP错误,无法修复");
    // }
    // }
    @Around("servicePointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        // List<Object> args = Arrays.asList(joinPoint.getArgs());
        Map<String, Object> argsValue = getArgsNameValue(joinPoint);
        StringBuffer sb = new StringBuffer();
        sb.append("执行方法:" + methodName + "< 开始");
        sb.append(NEWLINE + "参数值为:" + argsValue);

        // 执行目标方法
        // AtomicReference<Object> result = new AtomicReference<>();
        Object obj = null;
        try {
            obj = joinPoint.proceed();
            // result.set(proceedingJoinPoint.proceed());
            sb.append(NEWLINE + "返回结果为:" + JSONObject.toJSONString(obj));
        } catch (NotFoundException ex) {
            logger.error(ex.getMessage());
            throw new RuntimeException("服务提供者的服务实现AOP错误, 无法修复");
        } finally {
            sb.append("结束 >" + LINEFEED);
            logger.info(sb.toString());
        }

        return obj;

    }

    private Map<String, Object> getArgsNameValue(JoinPoint joinPoint) throws NotFoundException {
        String clazzName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        if (args.length == 0) {
            return null;
        }

        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(this.getClass());
        pool.insertClassPath(classPath);
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        Map<String, Object> map = new HashMap<>();
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++) {
            try {
                map.put(attr.variableName(i + pos), args[i]); // paramNames即参数名
            } catch (Exception e) {

            }
        }

        return map;
    }

}
